﻿using System;
using System.Data.Common;
using System.Web;
using System.Web.Caching;
using System.Collections.Generic;

namespace SDL
{
    public partial class ORM
    {
        /// <summary>
        /// Ключ кеша для SQL-команды
        /// </summary>
        /// <param name="cmd">SQL-команда</param>
        /// <returns>MD5 ключ</returns>
        private static string GetCacheKey(DbCommand cmd)
        {
            var sKey = cmd.CommandText;
            foreach (DbParameter param in cmd.Parameters)
            {
                sKey += param.ParameterName;
                sKey += param.DbType;
                sKey += param.Size;
                sKey += param.Value ?? "null";
            }
            return (sKey.MD5());
        }

        /// <summary>
        /// Ключ кеша для текущего объекта
        /// </summary>
        /// <returns>MD5 ключ</returns>
        public string GetCacheKey()
        {
            var type = GetType();
            var Members = type.GetMembers();
            var result = type.Name;
            foreach (var Member in Members)
            {
                var attrs = Member.GetCustomAttributes(typeof(CacheKeyAttribute), false);
                if (attrs.Length > 0) result += this.GetField(Member.Name);
            }
            return (result.MD5());
        }

        /// <summary>
        /// Берет из кеша результат SQL-команды
        /// </summary>
        /// <param name="cmd">SQL-команда</param>
        private object getCache(DbCommand cmd)
        {
            if (!this.IsCachable()) return (null);

            var cacheKey = GetCacheKey(cmd);
            return (HttpContext.Current.Cache[cacheKey]);
        }

        /// <summary>
        /// Сохраняет результат выполения SQL-команды
        /// </summary>
        /// <param name="cmd">SQL-команда</param>
        /// <param name="result">результат</param>
        private void setCache(DbCommand cmd, object result)
        {
            if (!this.IsCachable()) return;

            var key = GetType().ToString();

            if (HttpContext.Current.Cache[key] == null)
            {
                HttpContext.Current.Cache[key] = new object();
            }

            var cacheKey = GetCacheKey(cmd);
            var cacheDep = new CacheDependency(null, new[] { key });
            var timeExpire = new TimeSpan(0, 20, 0);

            HttpContext.Current.Cache.Add(cacheKey, result, cacheDep, Cache.NoAbsoluteExpiration, timeExpire, CacheItemPriority.Normal, null);

            if (log.IsDebugEnabled) log.Debug("CACHE SAVE COMMAND: '" + cmd.CommandText + ", commandCacheKey: " + GetCacheKey(cmd) + "', type: " + cmd.CommandType);
        }

        /// <summary>
        /// Сохраняет или обновляет объект в кеше
        /// </summary>
        /// <param name="oObject">объект</param>
        /// <returns>копия объекта</returns>
        private DBObject saveObjToCache(DBObject oObject)
        {
            if (!this.IsCachable()) return(null);

            var obj = HttpContext.Current.Cache[oObject.GetCacheKey()] as DBObject;

            if (obj == null)
            {
                HttpContext.Current.Cache[oObject.GetCacheKey()] = oObject;
                if (log.IsDebugEnabled) log.Debug("CACHE SAVE OBJECT: " + oObject.GetType() + ", cacheKey: " + oObject.GetCacheKey());
            }
            else
            {
                var diff = oObject.ReadFields.GetDifferences(obj.ReadFields);

                foreach (string o1 in diff.Keys)
                {
                    if (obj.ReadFields.ContainsKey(o1))
                        obj.ReadFields[o1] = diff[o1];
                    else
                        obj.ReadFields.Add(o1, diff[o1]);
                }
                if (diff.Count > 0)
                {
                    FillDataObject(obj, diff);
                    obj.ReadFromCache = true;
                    HttpContext.Current.Cache[obj.GetCacheKey()] = obj;
                    if (log.IsDebugEnabled) log.Debug("CACHE UPDATE OBJECT: " + oObject.GetType() + ", cacheKey: " + oObject.GetCacheKey());
                    return (obj);
                }
            }
            return (null);
        }

        /// <summary>
        /// Сохраняет или обновляет объекты из списка в кеше
        /// </summary>
        /// <param name="list">список объектов</param>
        private void saveObjsToCache(IList<DBObject> list)
        {
            if (!this.IsCachable()) return;

            for (var i = 0; i < list.Count; i++)
            {
                var obj = saveObjToCache(list[i]);
                if (obj != null) list[i] = obj;
            }
        }
    }
}
